import glob
import os
import datetime
import subprocess
import shutil

# 处理环境变量引用函数
def get_env_var(name,default=None,required=False):
    value = os.getenv(name,default)
    if required and value is None:
        raise RuntimeError(f"环境变量{name}未设置，请检查配置")
    return value
def skip_tables(config):
    """
    解析SKIP_TABLES配置，返回「数据库-跳过表」的对应字典
    输入格式（环境变量）：SKIP_TABLES="db1.table1,db1.table2,db2.table3"
    返回格式：{"db1": ["table1", "table2"], "db2": ["table3"]}
    """
    skip_dict = {}
    skip_config = config["skip_tables"].strip()
    if not skip_config:
        return skip_dict
    for item in skip_config.split(","):
        item = item.strip()
        if not item:
            continue
        if "." not in item:
            print(f"[{datetime.datetime.now()}] - 警告：跳过表配置格式错误（需为库.表），忽略无效配置：{item}")
            continue
        # 获取跳过库和表
        skip_db,skip_table = item.split(".",1)
        if not skip_db or not skip_table:
            print(f"[{datetime.datetime.now()}] - 警告：跳过表配置格式错误，忽略无效配置{item}")
            continue
        if skip_db not in skip_dict:
            skip_dict[skip_db] = []
        skip_dict[skip_db].append(skip_table)
    print(f"-------------------------------------{skip_dict}")
    return skip_dict
def main():
    config = {
        "mysql_host": get_env_var("MYSQL_HOST","127.0.0.1"),
        "mysql_port": int(get_env_var("MYSQL_PORT",3306)),
        "mysql_user": get_env_var("MYSQL_USER","root"),
        "mysql_password": get_env_var("MYSQL_PASSWORD",required=True),
        "mysql_databases": get_env_var("MYSQL_DATABASES",required=True),
        "mysql_backup_dir": get_env_var("MYSQL_BACKUP_DIR",default="/backups"),
        "retain_days": int(get_env_var("RETAIN_DAYS",7)),
        "skip_tables": get_env_var("SKIP_TABLES",""), # 格式 db1.table1,db2.table2
    }

    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M")
    backup_dir = os.path.join(config["mysql_backup_dir"],timestamp)
    os.makedirs(backup_dir, exist_ok=True)

    skip_dict = skip_tables(config)
    print(f"[{datetime.datetime.now()}] - 跳过表配置解析完成：{skip_dict}")

    # 2. 检查mysqldump工具是否存在（避免路径错误）
    mysqldump_path = "/usr/bin/mysqldump"
    if not os.path.exists(mysqldump_path):
        raise RuntimeError(f"未找到mysqldump工具，路径：{mysqldump_path}（请检查环境）")

    mysql_databases_list = [db.strip() for db in config["mysql_databases"].split(",") if db.strip()]
    for mysql_database in mysql_databases_list:
        backup_filename = f"backup_{mysql_database}.sql.gz"
        backup_path = os.path.join(backup_dir,backup_filename)

        # 4. 生成当前数据库的「跳过表参数」（--ignore-table=库.表）
        ignore_params = []
        if mysql_database in skip_dict:  # 仅当当前库有跳过表时才生成参数
            for skip_table in skip_dict[mysql_database]:
                ignore_params.append(f"--ignore-table={mysql_database}.{skip_table}")
        skip_info = ", ".join(skip_dict.get(mysql_database, ["无"]))
        print(f"[{datetime.datetime.now()}] - 开始备份数据库：{mysql_database}（跳过表：{skip_info}）")

        mysqldump_cmd = [
            "/usr/bin/mysqldump",
            f"--host={config['mysql_host']}",
            f"--port={config['mysql_port']}",
            f"--user={config['mysql_user']}",
            f"--password={config['mysql_password']}",
            f'"{mysql_database}"',
            *ignore_params,
            "|","gzip",">",backup_path
        ]
        full_cmd = " ".join(mysqldump_cmd)
        print(f"[{datetime.datetime.now()}] - 开始执行备份数据库：{mysql_database}")
        try:
            subprocess.run(
                full_cmd,
                check=True,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.PIPE,
                shell=True,
                cwd=backup_dir,
                text=True,
            )
            print(f"[{datetime.datetime.now()}] - 备份成功！备份文件路径：{backup_path}")
        except subprocess.CalledProcessError as e:
            print(f"[{datetime.datetime.now()}] - 备份失败！错误：{e.stderr}")
            return

    # 清理过期备份，保留 retain_days 天
    print(f"[{datetime.datetime.now()}] - 开始清理备份文件。。。（保留{config['retain_days']}天）")
    del_datetime = datetime.datetime.now() - datetime.timedelta(days=config["retain_days"])
    for dir_path in glob.glob(f"{config['mysql_backup_dir']}/*/"):
        dir_path = os.path.normpath(dir_path)
        if not os.path.isdir(dir_path) or os.path.islink(dir_path):
            continue
        dir_mtime = datetime.datetime.fromtimestamp(os.path.getmtime(dir_path))
        if dir_mtime < del_datetime:
            shutil.rmtree(dir_path)
            print(f"[{datetime.datetime.now()}] - 已删除过期目录：{dir_path}")

if __name__ == "__main__":
    main()
